<template>
    <div class="sea-table">
        <!-- header start -->
        <div class="flex-row">

            <!-- right-panel -->
            <div class="flex-right">

                <!-- table-title -->
                <div class="sea-table-title">
                    <slot name="title"></slot>

                    <!-- quick-search -->
                    <el-form @submit.native.prevent="onSubmit" :size="props.size" class="inline-block">
                        <el-input v-model="keyword" placeholder="请输入内容"
                                  style="min-width: 260px;margin-right: 12px;"
                                  :size="props.size">
                            <template #append>
                                <el-button :size="props.size" @click="onSubmit" :loading="loading">
                                    <i class="fa fa-search"></i>
                                </el-button>
                            </template>
                        </el-input>
                    </el-form>

                    <!-- other-buttons -->
                    <div class="inline-block">
                        <slot name="buttons"></slot>
                    </div>
                </div>
            </div>

            <!-- center-panel -->
            <div class="flex-body" style="padding: 0 10px;">
            </div>

            <!-- right-panel -->
            <div class="flex-right">
                <slot name="right-panel"></slot>

                <el-button v-if="props.params" title="展开" @click="switchExpand">
                    <i :class="expand? 'fa fa-chevron-up' : 'fa fa-chevron-down'"></i>
                </el-button>
            </div>
        </div>
        <!-- header end -->

        <el-form v-if="props.params" ref="form" :model="props.params" v-show="expand" :inline="true">

            <!-- form-input -->
            <slot name="input"></slot>

            <el-form-item>
                <el-button :size="props.size" type="primary" @click="onSubmit" :loading="loading">查询</el-button>
                <el-button :size="props.size" @click="onReset" :loading="loading">重置</el-button>
            </el-form-item>
        </el-form>

        <!-- body -->
        <div v-loading="loading" class="sea-table-body">
            <el-col>
                <slot :list="list"></slot>
            </el-col>
        </div>
        <!-- body -->

        <!-- footer -->
        <div v-if="pagination" class="sea-table-footer">
            <el-pagination
                :total="total" :page-sizes="pageSizes"
                :page-size="query_limit" @size-change="handleLimitChange"
                :current-page="query_page" @current-change="handlePageChange"
                layout="total, sizes, prev, pager, next, jumper"/>
        </div>
        <!-- footer -->
    </div>
</template>


<script setup lang="ts">
/* eslint-disable */
import {ref, watch, onMounted, nextTick} from "vue";
import Layers from "@/lang/layers/index";
import setting from "@/widget/provider/setting";

/**
 * 网络请求框架
 *
 * 已经完 header-form 和 footer-pagination 的设计，
 * 后续开发只需要关注 body 部分即可。
 *
 * 当前组件和 el-table 组合，可以达到 sea-table 一样的效果，并且这种组合更优
 *
 * 当前设计：
 * 表单输入框在两行以内的时候，能呈现较好效果
 */
interface Props {
    // 数据 url
    url?: string
    // 请求方式
    method?: string
    // 响应式参数，值的改变会触发查询
    where?: Record<string, any>
    // 表单默认值，首次查询所需的参数（改变不会触发查询）
    params?: Record<string, any>
    // 本地数据，直接指定数据列表
    data?: any[]
    // 是否激活，退出后清空数据
    active?: boolean

    // 按钮组件大小
    size?: string

    // 是否需要分页
    pagination?: boolean
    // 当前排序
    sort?: string
    // 当前页码
    page?: number
    // 每一页容量
    limit?: number
    // 排序字段名
    sortName?: string
    // 页号字段名称
    pageName?: string
    // 页容量字段名称
    limitName?: string
    // 页号选项
    pageSizes?: number[]

    // 挂载后自动触发查询
    autoLoad?: boolean
    // 数据查询执行器
    provider?: (args: any) => Promise<any>
    // 提交表单前置拦截，如果不满足条件，可以阻止表单提交
    intercept?: (args: any) => boolean | void
    // 查询结果格式化
    resultParser?: (response: any) => any
}

const props = withDefaults(defineProps<Props>(), {
    method: 'GET',
    pagination: true, sort: '', page: 1, limit: 10,
    sortName: 'sort', pageName: 'page', limitName: 'limit', pageSizes:  () => [10, 20, 50, 100],
    autoLoad: true, active: undefined,
    provider: setting.ajax, resultParser: setting.paginationParser
})

// 数据列表，通过插槽回传数据
const list = ref(props.data);
// 数据总量
const total = ref(0);

// 默认值
const default_value = {};

// 表单初值
if (props.params === undefined) {
    Layers.assign(props.params, default_value);
}

// 展开表单
const expand = ref(false);
// 表格数据加载中
const loading = ref(false);

// 关键字
const keyword = ref('');

// 分页：当前页号
const query_page = ref(props.page);
// 分页：当前页大小
const query_limit = ref(props.limit);

// 绑定联动的参数
let query_where = props.where || {};
// 表单初始值
let query_params = props.params || {};
// 分页信息
let query_attach = {
    [props.sortName]: props.sort,
    [props.pageName]: props.page,
    [props.limitName]: props.limit
};


/**
 * 首次载入数据
 *
 * 首次加载是非常特殊的，
 * 需要获取分页、表单等各方的初始化数据，
 * 这些数据分布在各个插槽中，
 *
 * 插槽中的组件，生命周期是不可控的，在调用这个函数的时候，组件可能还没渲染完成，
 *
 * 因此这个函数很容易产生空指针异常，写法上，尽量保证所有数据载入完成。
 *
 * tips：VUE3 基本没这方面的问题
 */
const firstLoad = () => {
    if (props.autoLoad && Layers.isNotEmpty(props.url)) {
        reload();
    }
};


/**
 * 重新载入数据
 *
 * @param values 参数
 */
const reload = (values) => {
    // 正在加载
    if (loading.value) {
        return;
    }
    loading.value = true;

    // 构建请求参数
    let params = {};

    // 分页参数
    Layers.assign(query_attach, params);
    // 表单参数
    Layers.assign(query_params, params);
    // 额外指定的参数
    Layers.assign(query_where, params);
    // 微调参数
    Layers.assign(values, params);

    console.log(params);

    let request = {
        url: props.url,
        method: props.method,
        params: params
    }

    // 前置拦截，进行逻辑判断，根据内容决定是否提交
    if (props.intercept !== undefined && props.intercept(request) === false) {
        return;
    }

    // ajax参数
    props.provider(request).then(response => {
        let resp = props.resultParser(response);

        total.value = resp.total;
        list.value = resp.list;
    }).finally(() => {
        loading.value = false;
    })
}

/**
 * 表单数据发生变化，并触发查询
 *
 * @param params 表单数据
 */
const handlerParams = (params) => {
    query_params = params;
    reload();
};

/**
 * 触发查询，附加参数发生变化
 *
 * @param where 附加参数
 */
const handlerWhere = (where) => {
    query_where = where;
    reload();
};

/**
 * 页号发生变化
 *
 * @param page 页号
 */
const handlePageChange = (page) => {
    query_attach[props.pageName] = page;
    reload()
};

/**
 * 分页容量发生变化
 *
 * @param limit 容量
 */
const handleLimitChange = (limit) => {
    query_attach[props.limitName] = limit;
    reload()
};

/**
 * 数据排序发生变化
 *
 * @param sort 排序
 */
const handleSortChange = (sort) => {
    query_attach[props.sortName] = sort;
    reload()
};

/**
 * 展开搜索栏，默认搜索栏处于隐藏状态
 */
const switchExpand = () => {
    expand.value = !expand.value;
};

/**
 * 点击了查询按钮
 */
const onSubmit = () => {
    query_params.keyword = keyword.value;
    reload()
};

/**
 * 点击了重置按钮
 */
const onReset = () => {
    Layers.assign(default_value, query_params);
    reload();
};


// Watchers 每一个侦听，触发的时候都会刷新表格
watch(() => props.where, handlerWhere);

watch(() => props.page, handlePageChange);

watch(() => props.limit, handleLimitChange);

watch(() => props.sort, handleSortChange);

if(props.active !== undefined){
    watch(() => props.active, (val) => {
        if (!val) {
            list.value = [];
        }
    });
}

onMounted(() => {
    nextTick(firstLoad);
});

defineExpose({reload});
</script>